/*
 * arg_parser.c
 *
 *  Created on: Aug 28, 2012
 *      Author: Adrian Ortega Garcia
 */

#include <argp.h>
#include <stdlib.h>
#include "arg_parser.h"
#include "logging.h"

/* Global variables */
dissassembler_arguments	arguments;

const char *argp_program_version 		= "dissassembler v1.0";
const char *argp_program_bug_address 	= "<elfus0.1@gmail.com>";

/* Program documentation. */
static char prog_doc[] = "dissassembler -- A dissassembler implementation for ELF x86 binaries";

/* Usage example */
static char args_doc[] = "<file(s)>";

/* long option name, short option name, ..., flags, documentation string */
const struct argp_option options [] = {
		{"elf-header", 'h', 0 , 0, "Prints the ELF header of the given files."},\
		{"section-header", 's', 0 , 0, "Prints the section header of the given files."},\
		{"program-header", 'p', 0 , 0, "Prints the program header of the given files."},\

		{"feature", 'f', "NUMBER", 0, "Choose the number of feature to use."\
									"Use dissassembler -d to display all the features."},
		{"log-level", 'l', "LEVEL", 0,
				"Sets the amount of information printed on screen. LEVEL can be a number: "\
				"0 (Errors), "\
				"1 (Warnings), "\
				"2 (General Information) [DEFAULT], "\
				"3 (Debug information), "\
				"4 (Trace information), "\
				"5 (Data information)."
		},
		{"verbose", 'v', 0, 0,
				"Produce verbose output. It has the same effect"\
				" as --log-level=5"		},
		{"display-features", 'd', 0, 0, "Displays all features in the dissassembler"},
		{0} //!< Last entry needs to be 0, either way it will do segmentation fault
};

static int mem_allocated = 0;


static error_t parser (int key, char *arg, struct argp_state *state);

// \brief Argument parser
static struct argp argp = {
						options,  //!< The options our program accepts
						&parser,  //!< The parser
						args_doc, //!< Used for the 'Usage: ' message
						prog_doc  //!< Program information
					};


error_t parser (int key, char *arg, struct argp_state *state)
{
	/*
	 * Get the input argument from argp_parse, which we know is a
	 * pointer to our arguments structure.
	 */
	dissassembler_arguments *parguments =(dissassembler_arguments*)state->input;

	switch(key)
	{
	// Make sure the case statements match the argp_options structure.
		case 'v':
			// Verbose has the same effect as setting log leve to the highest.
			parguments->log_level = 5;
			break;
		case 'd':
			parguments->display_features = 1;
			break;
		case 'l':
			parguments->log_level = atoi(arg);
			break;
		case 'f':
			parguments->feature = atoi(arg);
			break;
		case 'h':
			parguments->feature = PRINT_ELF_HDR;
			break;
		case 's':
			parguments->feature = PRINT_SECTION_HDR;
			break;
		case 'p':
			parguments->feature = PRINT_PROGRAM_HDR;
			break;
		case ARGP_KEY_ARG:
			arguments.num_arguments++;
			file_t *tmp= (file_t*) malloc(sizeof(file_t));
			mem_allocated += sizeof(file_t);
			list_add_file(tmp);
			tmp->file_name = arg;
			break;

		case ARGP_KEY_END:
			if( arguments.num_arguments == 0 && !arguments.display_features ){
				LOG_WARN("Not enough files.\n");
				argp_usage(state);
			}
			break;
		default:
			return ARGP_ERR_UNKNOWN;
			break;
	}
	return 0;
}

int parse_arguments(int argc, char *argv[])
{
	int rc = 0;

	// Initialize default arguments
	arguments.num_arguments = 0;
	arguments.feature = 0;
	arguments.display_features = 0;
	arguments.verbose  = 0;
	arguments.log_level = LOG_LEVEL_INFO;
	arguments.files	= 0;

	// Parse the arguments
	rc = argp_parse(&argp, //!< Argument parser 'argp' structure
				argc, //!< Agument count
				argv, //!< Argument vector
				0,	  //!< Flags
				0,	  //!< Argument index. If arg_index is non-null, the index of the first unparsed option in argv is returned as a value.
				&arguments);   //!< Pointer to a structure which is used for specifying parameters to the parser and passing back the results.
	return rc;
}

void	list_add_file(file_t *new_file)
{
	if(!(arguments.files)){
		/* Empty list */
		arguments.files = new_file;
		new_file->next = NULL;
	} else {
		/* Add register field to the end of the list */
		file_t *list_p;
		for(list_p = arguments.files; list_p != NULL; list_p = list_p->next){
			file_t *current = list_p;
			if( current->next == NULL ){
				current->next = new_file;
				new_file->next = NULL;
				break;
			}
		}
	}
}

void list_free_files(file_t *first)
{
	file_t * current = first;
	while(current != NULL){
		file_t *next = current->next;
		free((void*)current);
		mem_allocated -= sizeof(file_t);
		current = next;
	}
	LOG_DEBUG("Remaining bytes allocated: %d", mem_allocated);
	first = 0;
}

void 	list_print_files(file_t *first)
{
	file_t *current = arguments.files;
	LOG_INFO("Files passed through command line:\n");
	while(current != NULL){
		LOG_INFO("%s\n", current->file_name);
		current = current->next;
	}
}


